package com.netflexitysolutions.amazonws.sdb.internal.iterators;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.netflexitysolutions.amazonws.sdb.ResponseCallback;
import com.netflexitysolutions.amazonws.sdb.SimpleDB;
import com.netflexitysolutions.amazonws.sdb.SimpleDBException;
import com.netflexitysolutions.amazonws.sdb.internal.operations.OperationExecutor;

public abstract class AbstractListIterator<REQUEST_TYPE, ITERABLE_ITEM_TYPE> implements Iterator<List<ITERABLE_ITEM_TYPE>> {
	
	private Logger logger = LoggerFactory.getLogger(DomainListIterator.class);
	
	private ResponseCallback callback;
	private SimpleDB simpleDB;
	private OperationExecutor<REQUEST_TYPE, List<ITERABLE_ITEM_TYPE>> operation;
	private List<ITERABLE_ITEM_TYPE> items;
	private List<SimpleDBException> errors = new ArrayList<SimpleDBException>();
	
	/**
	 * @param simpleDB
	 * @param callback
	 */
	public AbstractListIterator(SimpleDB simpleDB, ResponseCallback callback){
		this.simpleDB = simpleDB;
		this.callback = callback;
	}

	/**
	 * @throws SimpleDBException
	 */
	public void init() throws SimpleDBException{
		// Create operation.
		operation = prepareOperation();
		
		// Get first set of results.
		try{
			items = operation.execute();
			
			// Call back (notify) with operation statistics.
			callback.onResponse(operation.getStats());
		}
		catch (SimpleDBException e) {
			errors.add(e);
			logger.error(e.getMessage(), e);
			throw e;
		}
	}
	
	/**
	 * @return
	 */
	protected abstract OperationExecutor<REQUEST_TYPE, List<ITERABLE_ITEM_TYPE>> prepareOperation();
	
	/* (non-Javadoc)
	 * @see java.util.Iterator#hasNext()
	 */
	public boolean hasNext() {
		if(!errors.isEmpty()){
			return false;
		}
		else if((items != null && !items.isEmpty())
				|| StringUtils.isNotBlank(operation.getNextToken())){
			return true;
		}
		else{
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see java.util.Iterator#next()
	 */
	public List<ITERABLE_ITEM_TYPE> next() {
		if(hasNext()){
			List<ITERABLE_ITEM_TYPE> itemsToReturn = null;
			if(items != null){
				itemsToReturn = items;
				items = null;
				errors.clear();
			}
			else{
				try{
					items = operation.execute();
					itemsToReturn = items;
					items = null;
					errors.clear();
					
					// Call back (notify) with operation statistics.
					callback.onResponse(operation.getStats());
				}
				catch (SimpleDBException e) {
					errors.add(e);
					logger.error(e.getMessage(), e);
				}
			}
			return itemsToReturn;
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see java.util.Iterator#remove()
	 */
	public void remove() {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * @return the errors
	 */
	public List<SimpleDBException> getErrors() {
		return errors;
	}

	/**
	 * @return the callback
	 */
	public ResponseCallback getCallback() {
		return callback;
	}

	/**
	 * @return the simpleDB
	 */
	public SimpleDB getSimpleDB() {
		return simpleDB;
	}
}
